import { mapState } from 'vuex'
import { checkPermi } from '@/utils/permission'
import gridOptions from '@/utils/grid-options'
import { camel2snake } from '@/utils/camel-case'
import { cloneDeep } from 'lodash'
const ExcelJS = require('exceljs')
import FileSaver from 'file-saver'
import Sortable from 'sortablejs'
import VXETable from 'vxe-table'

const searchButton = {
  span: 24,
  align: 'center',
  collapseNode: true,
  itemRender: {
    name: '$buttons',
    children: [
      {
        props: {
          type: 'submit',
          content: 'app.body.label.search',
          status: 'primary',
          icon: 'vxe-icon-search',
        },
      },
      {
        props: {
          type: 'reset',
          content: 'app.body.label.reset',
          icon: 'vxe-icon-refresh',
        },
      },
    ],
  },
}

export const gridTable = {
  data() {
    return {
      gridTableData: [],
      fetchData: {},
      exportTotal: 0,
      gridOptions: cloneDeep(gridOptions),
      xGrid: {},
      addEditFormDialog: {
        open: false,
        options: {},
        operationType: 1,
      },
    }
  },
  computed: {
    ...mapState([]),
  },
  created() {
    this.initGrid()
  },
  mounted() {
    this.$nextTick(() => {
      this.getGrid()
      this.columnDrop()
    })
  },
  methods: {
    handleExcelExport() {
      if (!this.gridOptions.toolbarConfig.export) {
        return
      }

      const {
        meta: { title },
        name,
      } = this.$router?.currentRoute || { meta: { title: '' } }
      this.gridOptions.id = this.type ? `${name}_${this.type}` : name
      // 处理表格导出excel
      const { buttons } = this.gridOptions?.toolbarConfig || { buttons: [] }

      if (buttons[0] && buttons[0].permi) {
        const arr = buttons[0].permi[0].split(':')
        arr[2] = 'export'
        this.gridOptions.toolbarConfig.export = checkPermi([arr.join(':')])
      }

      const modes = ['current', 'selected']
      if (this.fetch?.getList) {
        modes.push('all')
      }

      this.gridOptions.exportConfig = {
        original: false,
        useStyle: true,
        remote: true,
        types: ['xlsx'],
        modes,
        filename: title,
        sheetName: title,
        exportMethod: this.exportMethod,
      }
    },
    async initGrid() {
      this.handleExcelExport()
      this.checkToolbarConfigPermi()

      if (this.gridOptions?.proxyConfig?.autoLoad !== false) {
        if (
          !('dialogFormVisible' in this) &&
          this.fetch?.getList &&
          !this.gridOptions.height
        ) {
          const height = document.documentElement.clientHeight - 190
          this.gridOptions.height = `${height}px`
        }
        this.gridOptions.proxyConfig.ajax = {
          query: async () => {
            return await this.fetchGridData()
          },
        }
      } else {
        this.gridOptions.proxyConfig = undefined
      }

      let items = this.gridOptions?.formConfig?.items.filter(
        (item) => !('visible' in item && item.visible === false)
      )
      if (items.length) {
        const searchBtn = cloneDeep(searchButton)
        if (items.length < 3) {
          searchBtn.span = 8
          searchBtn.align = 'left'
        }

        if (items.length <= 4) {
          searchBtn.collapseNode = false
        }

        items.map((item, index) => {
          const { name, props } = item.itemRender || {}
          item.folding = index > (items.length <= 3 ? 2 : 3)
          item.span = items.length <= 3 ? 8 : 6
          switch (name) {
            case '$select':
            case '$input':
              item.itemRender.props = {
                placeholder: `请${name === '$input' ? '输入' : '选择'}${
                  item.title
                }`,
                clearable:
                  'clearable' in item.itemRender
                    ? item.itemRender.clearable
                    : true,
                ...props,
              }
          }
        })

        const index = this.gridOptions.formConfig.items.findIndex(
          (item) => item.itemRender.name === '$buttons'
        )
        if (index === -1) {
          this.gridOptions.formConfig.items.push(searchBtn)
        }
      }

      if (!this.gridOptions.noSortable) {
        this.gridOptions.columns = this.gridOptions.columns.map((item) => {
          if (!('sortable' in item)) {
            item.sortable = !['操作', '备注'].includes(item.title)
          }
          return item
        })
      }
    },
    getGrid() {
      if (this.$refs?.xGrid) {
        if (this.$refs?.xGrid?.commitProxy) {
          this.xGrid = this.$refs.xGrid
        } else {
          const { $refs } = this.$refs.xGrid
          this.xGrid = $refs.xGrid
        }
      }
      return this.xGrid
    },
    handleFormReset({ data }) {
      const values = Object.values(data).filter((item) => !!item)
      if (!values.length) {
        return
      }
      for (const key in data) {
        data[key] = null
      }
      this.fetchGridData()
    },
    setFormConfigSelectItemOptions(name, dictName = null) {
      const index = this.gridOptions.formConfig.items.findIndex(
        (item) => item.field === name
      )
      this.gridOptions.formConfig.items[index].itemRender.options =
        this.dict.type[dictName ? dictName : camel2snake(name)]
    },
    setFormConfigSelectItemValue(name, value) {
      const index = this.gridOptions.formConfig.items.findIndex(
        (item) => item.field === name
      )
      this.gridOptions.formConfig.items[index].itemRender.options = value
    },
    checkToolbarConfigPermi() {
      let { buttons, tools } = this.gridOptions.toolbarConfig
      const CheckPermi = (arr) =>
        (arr || []).map((item) => {
          if (item.permi) {
            item.visible = checkPermi(item.permi)
          }
          return item
        })
      // 校验左侧增删改查是否有操作权限
      buttons && (buttons = CheckPermi(buttons))
      // 校验右侧配置的按钮是否有操作权限
      tools && (buttons = CheckPermi(tools))
    },
    async handleToolbarButtonClick({ code, $grid }) {
      const rows = $grid.getCheckboxRecords()
      switch (code) {
        case 'add':
          this.handleAdd()
          break
        case 'update':
          if (!rows.length) {
            this.$message({
              message: '请选择要修改的数据',
              type: 'warning',
            })
            return
          }

          if (rows.length > 1) {
            this.$message({
              message: '只能选择一条进行修改',
              type: 'warning',
            })
            return
          }
          this.handleUpdate(rows[0])
          break
        case 'del':
          if (!rows.length) {
            this.$message({
              message: '请选择要删除的数据',
              type: 'warning',
            })
            return
          }
          this.handleDelete($grid.getCheckboxRecords())
          break
        default:
          this.handleClickToolbarButton?.({ code, $grid })
      }
    },
    async handleToolbarToolClick({ tool, code, $grid }) {
      switch (code) {
        case 'download':
          const { form, pager } = $grid.getProxyInfo()
          this.handleExport(
            {
              ...form,
              pageNum: pager.currentPage,
              pageSize: this.exportTotal || pager.pageSize,
            },
            tool
          )
          break
        default:
          this.handleClickToolbarTool?.({ code, $grid })
      }
    },
    async fetchGridData() {
      this.getGrid()
      const { form, pager: page } = this.xGrid.getProxyInfo()
      const params =
        this.workFormParams instanceof Function
          ? this.workFormParams({
              ...form,
              ...page,
            })
          : form
      const res = await this.fetch.getList({
        ...params,
        pageNum: page.currentPage,
        pageSize: page.pageSize,
      })

      this.exportTotal = res.total
      this.gridTableData = res.rows
      this.fetchData = res
      const data =
        this.filterFetchData instanceof Function
          ? await this.filterFetchData(res)
          : res

      this.xGrid.reloadData(data.rows)
      return data
    },
    async handleAdd() {
      this.handleAddBefore instanceof Function
        ? await this.handleAddBefore()
        : ''
      this.addEditFormDialog.operationType = 1
      this.addEditFormDialog.options = {}
      this.addEditFormDialog.open = true
      this.addEditFormDialog.view = false
    },
    getViewFormDesc(columns = []) {
      return [...this.gridOptions.columns, ...columns]
        .filter((item) => !['序号', '', '操作'].includes(item.title))
        .filter((item) => item.view !== false)
        .reduce((pre, curr) => {
          const { slots, ...rest } = curr

          pre[curr.field] = {
            ...rest,
            type:
              curr.type && !['spe'].includes(curr.type) ? curr.type : 'text',
            label: curr.title,
            layout: curr.layout || 12,
          }

          if (curr.slots?.default === 'slot-scope') {
            pre[curr.field].options = Object.entries(
              this.getStatusMap(curr.params)
            ).map((item) => ({
              text: item[1],
              value: Number(item[0]),
            }))
          }
          return pre
        }, {})
    },
    async handleUpdate(row, type = 2, columns = []) {
      this.handleUpdateBefore instanceof Function
        ? await this.handleUpdateBefore(row)
        : ''
      const { data } = await this.fetch.getRowData(row[this.fetch.id])
      this.addEditFormDialog.options = {
        ...row,
        ...data,
        ...(row.__params || {}),
      }
      if (type === 3) {
        this.addEditFormDialog.options.viewFormDesc =
          this.getViewFormDesc(columns)
      }
      this.addEditFormDialog.operationType = type
      this.addEditFormDialog.open = true
    },
    async handleView(row) {
      let data = cloneDeep(row)
      if (this.handleViewBefore instanceof Function) {
        data = await this.handleViewBefore(data)
      }

      this.handleUpdate(data, 3)
    },
    async handleDelete(row) {
      this.handleDeleteBefore instanceof Function
        ? await this.handleDeleteBefore(row)
        : ''
      await this.$modal.confirm(
        `是否确认删除${
          Array.isArray(row) ? '所选' : '该行'
        }数据，删除之后不能恢复？`
      )

      const ids = Array.isArray(row)
        ? row.map((item) => item[this.fetch.id])
        : row[this.fetch.id]
      await this.fetch.delete(ids)
      this.xGrid.remove(row)
      this.$modal.msgSuccess('删除成功')
    },
    async handleUpdateBefore(row) {
      await this.handleAddBefore(row)
    },
    async handleAddBefore(row) {
      if (this.handleDialogShow instanceof Function) {
        this.$modal.loading('请稍候...')
        try {
          await this.handleDialogShow(row)
        } catch (error) {}
        this.$modal.closeLoading()
      }
    },
    // 获取字典的键值对
    getStatusMap(type) {
      const curr = this.$store.state.dict.dict.find((item) => item.key === type)
      return curr?.value.reduce((prev, next) => {
        prev[next.dictValue] = next.dictLabel
        return prev
      }, {})
    },
    handleExport(params, tool) {
      this.download(
        tool.url,
        params,
        `${tool.url.split('/')[1]}_${new Date().getTime()}.xlsx`
      )
    },
    addTable({
      ref = 'A1',
      name = 'MyTable',
      headerRow = true,
      useStyle = true,
      original = false,
      ws,
      wb,
      columns,
      data,
    }) {
      const obj = {
        name,
        ref,
        headerRow,
        style: {
          showFirstColumn: true,
          showColumnStripes: true,
          theme: useStyle ? 'TableStyleMedium2' : '',
          showRowStripes: useStyle,
        },
        columns: columns.map((item) => ({
          name: item.title,
          filterButton: true,
          width: 120,
        })),
        rows: data.map((item, index) =>
          columns.map((it) => {
            if (original) {
              return item[it.field]
            }

            if (it.slots?.default === 'slot-scope') {
              return this.getStatusMap(it.params)[item[it.field]]
            } else if (it.slots?.default === 'slot-select') {
              return this.$store.state.cache[it.params].map[item[it.field]]
            } else if (it.type === 'seq') {
              return index + 1
            } else if (it.type === 'gallery') {
              ws.addImage(
                wb.addImage({
                  base64: item[it.field],
                  extension: 'png',
                }),
                {
                  tl: { col: ind, row: index + 1 },
                  ext: { width: 80, height: 40 },
                }
              )
              return
            }

            return item[it.field]
          })
        ),
      }
      ws.addTable(obj)
      return obj
    },
    async handleFileSaver({ wb, filename }) {
      const buffer = await wb.xlsx.writeBuffer()
      FileSaver(
        new Blob([buffer], {
          type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8',
        }),
        `${filename}.xlsx`
      )
      return true
    },
    async exportMethod({ options }) {
      const $grid = this.getGrid()
      const proxyInfo = $grid.getProxyInfo()
      let {
        filename,
        sheetName,
        isHeader: headerRow,
        original,
        mode,
        columns: col,
        useStyle,
        data,
      } = options

      switch (mode) {
        case 'selected':
          data = $grid.getCheckboxRecords()
          break
        case 'all':
          const {
            form,
            pager: { currentPage, total },
          } = proxyInfo || {
            pager: { currentPage: 1, total: 1000 },
            form: {},
          }
          const { rows } = await this.fetch.getList({
            ...form,
            pageNum: currentPage,
            pageSize: total,
          })
          data = rows
          break
        default:
          break
      }
      const columns = col.filter((item) => !['', '操作'].includes(item.title))
      const wb = new ExcelJS.Workbook()
      const ws = wb.addWorksheet(sheetName, {
        properties: { tabColor: { argb: '#057c52' } },
      })

      this.addTable({ ws, wb, headerRow, useStyle, columns, data, original })
      return this.handleFileSaver({ wb, filename })
    },
    columnDrop() {
      const $table = this.getGrid()
      if (!Object.keys($table).length) {
        return
      }
      this.sortable = Sortable.create(
        $table.$el.querySelector(
          '.body--wrapper>.vxe-table--header .vxe-header--row'
        ),
        {
          handle: '.vxe-header--column',
          onEnd: ({ item, newIndex, oldIndex }) => {
            const { fullColumn, tableColumn } = $table.getTableColumn()
            const targetThElem = item
            const wrapperElem = targetThElem.parentNode
            const newColumn = fullColumn[newIndex]
            if (newColumn.fixed) {
              const oldThElem = wrapperElem.children[oldIndex]
              // 错误的移动
              if (newIndex > oldIndex) {
                wrapperElem.insertBefore(targetThElem, oldThElem)
              } else {
                wrapperElem.insertBefore(
                  targetThElem,
                  oldThElem ? oldThElem.nextElementSibling : oldThElem
                )
              }

              VXETable.modal.message({
                content: '固定列不允许拖动，即将还原操作！',
                status: 'error',
              })
              return
            }
            // 获取列索引 columnIndex > fullColumn
            const oldColumnIndex = $table.getColumnIndex(tableColumn[oldIndex])
            const newColumnIndex = $table.getColumnIndex(tableColumn[newIndex])
            // 移动到目标列
            const currRow = fullColumn.splice(oldColumnIndex, 1)[0]
            fullColumn.splice(newColumnIndex, 0, currRow)
            $table.loadColumn(fullColumn)
          },
        }
      )
    },
  },
}
